package bma.common.langutil.jmshell.command;

import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;

import bma.common.langutil.core.StringUtil;
import bma.common.langutil.core.ValueUtil;
import bma.common.langutil.jmshell.Session;
import bma.common.langutil.jmshell.cmdopt.CommandOptionParser;

public class HistoryCommand extends AbstractSimpleCommandProcessor {

	public static final String DATA_KEY = "@history_data";
	public static final String LAST_COMMAND = "/";

	protected static class CommandInfo {
		public int id;
		public String command;
	}

	protected static class HistoryInfo {
		public int id;
		public int maxSize = 50;
		public LinkedList<CommandInfo> commands = new LinkedList<CommandInfo>();
	}

	public HistoryCommand() {
		super("history");
		setArgsTips("[runHistoryNum]");
	}

	@Override
	public CommandOptionParser createCommandOption(Session session) {
		CommandOptionParser r = new CommandOptionParser();
		r.addIntegerOption('s', "size", "list history size");
		r.addIntegerOption('m', "max", "set history max size");
		return r;
	}

	@Override
	public boolean processCommand(Session session, String cmd,
			CommandOptionParser options) {
		int size = options.getOptionValue("size", 10);
		int maxn = options.getOptionValue("max", -1);
		if (maxn > 0) {
			setMax(session, maxn);
			return true;
		}
		String[] args = options.getRemainingArgs();
		int id = 0;
		if (args != null && args.length > 0) {
			id = ValueUtil.intValue(args[0], 0);
			if (id == 0) {
				session.writeln("ERROR: '" + args[0] + "' must is number");
			}
			run(session, id);
			return true;
		}
		list(session, size);
		return true;
	}

	static protected HistoryInfo getData(Session s, boolean sure) {
		HistoryInfo r = (HistoryInfo) s.getAttribute(DATA_KEY);
		if (r == null && sure) {
			r = new HistoryInfo();
			s.setAttribute(DATA_KEY, r);
		}
		return r;
	}

	public void setMax(Session s, int maxn) {
		HistoryInfo data = getData(s, true);
		data.maxSize = maxn;
		while (data.commands.size() > maxn) {
			data.commands.removeFirst();
		}
		s.writeln("set history max size done => " + maxn);
	}

	public void list(Session s, int size) {
		HistoryInfo info = getData(s, false);
		s.writeln("[history]");
		if (info != null) {
			List<CommandInfo> list = info.commands;
			DecimalFormat df = new DecimalFormat("#####");
			if (list.size() > size) {
				list = list.subList(list.size() - size, list.size());
			}
			for (CommandInfo cmd : list) {
				s.writeln(df.format(cmd.id) + ": " + cmd.command);
			}
		}
		s.writeln("[history end]");
	}

	public void run(Session s, int num) {
		HistoryInfo info = getData(s, false);
		if (info != null) {
			for (CommandInfo cmd : info.commands) {
				if (cmd.id == num) {
					s.writeln("[history run] >> " + cmd.command);
					s.execute(cmd.command);
					return;
				}
			}
			s.writeln("ERROR: can't find history '" + num + "'");
		}
	}

	public static String lastCommand(Session s) {
		HistoryInfo info = getData(s, false);
		if (info != null) {
			if (!info.commands.isEmpty()) {
				CommandInfo cinfo = info.commands.getLast();
				return cinfo.command;
			}
		}
		return null;
	}

	public static void pushCommand(Session s, String command) {
		if (command == null)
			return;
		if (command.equals(LAST_COMMAND))
			return;

		String lcmd = lastCommand(s);
		if (StringUtil.equals(lcmd, command))
			return;
		HistoryInfo info = getData(s, true);
		CommandInfo cinfo = new CommandInfo();
		cinfo.id = ++info.id;
		cinfo.command = command;
		info.commands.addLast(cinfo);
		if (info.commands.size() > info.maxSize) {
			info.commands.removeFirst();
		}
	}

	public static String makeCommand(Session session, String command) {
		if (command == null)
			return null;
		if (!command.equals(LAST_COMMAND)) {
			return command;
		}
		String cmd = lastCommand(session);
		if (cmd == null) {
			session.writeln("ERROR: no last command");
			return command;
		}
		session.writeln(">> " + cmd);
		return cmd;
	}
}